JavaScript 在運作時會建立執行環境,分為 Global Execution Context(全域) 和 Function Execution Context(函式)兩種。
而在執行環境建立的時候又分兩個階段,分別是 Creation Phase 和 Execution Phase,在 Creation Phase 時會建立 this 以及變數和函式的記憶體位置。
例如:
在 function 被執行前會先到 creation phase ,程式碼正式被運行後,才會到 execution phase 這個階段,並遵循 callstack 原則一行一行執行。
Hoisting 發生在 Creation phase
在執行程式碼之前(creation phase)電腦的記憶體只會分配給 function declaration variable 和 var,並不會分配給 let,const 和 function expression。
例如:
會 hoist 的情況
console.log(x); // undefined
var x = 10;
console.log(x); // 10
在程式碼被執行之前, JavaScript 會先找到 var ,再找到 x 這個變數,並放到記憶體裡面,但 assignment 這件事情先不管,所以當第 1 行 console.log(x) 時會看到 undefined,因為電腦已經知道 x 是個變數,但裡面還沒被做 assignment
接著執行第 2 行時,就做 assignment,把 10 放進 x 裡面,第 3 行 console.log(x) 就會看到 10 這個數字
sayHi();
function sayHi(){
console.log("Hi");
}
// Hi
在程式碼被執行之前, JavaScript 會先找到 sayHi 這個 function 並丟到記憶體裡面,當第 1 行被執行時,電腦會知道 sayHi 這個 function 裡面的東西,所以會出現 Hi 的結果
不會 hoist 的情況
console.log(x);
let x = 10;
// Uncaught ReferenceError: Cannot access 'x' before initialization
sayHi();
const sayHi = ()=>{
console.log("Hi");
}
// Uncaught ReferenceError:Cannot access 'sayHi' before initialization
let myName = "Helen";
function sayHi(){
console.log("sayHi: " + myName);
function sayHi2(){
console.log("sayHi2: " + myName);
}
sayHi2(); // sayHi2: Helen
}
sayHi(); // sayHi: Helen
myName 是全域變數,在任何地方都是有意義的,所以無論在哪裡都可以找到 myName
function sayHi(){
let myName = "Helen";
console.log(myName);
}
sayHi(); // Helen
console.log(myName); // Uncaught ReferenceError: myName is not defined
if(true){
let x = 10;
console.log(x); // 10
}
console.log(x); // Uncaught ReferenceError: x is not defined
var 沒有 block scope,因此使用 var 會有很多問題
if(true){
var x = 10;
console.log(x); // 10
}
console.log(x); // 10
在 Creation phase 會發生的事情
let myName = "Helen";
function sayHi(){
let myName = "Mary";
console.log("sayHi: " + myName);
sayHi2();
}
function sayHi2(){
console.log("sayHi2: " + myName);
}
sayHi();
// sayHi: Mary
// sayHi2: Helen
如果在 function 中的變數並沒有在 function 中被定義,則會跑去 function 被定義的地方找,function sayHi2 裡的 myName 並沒有在此 function 中被定義,所以會跑到 sayHi2 被定義的地方找(window),找到 myName 是 Helen
let myName = "Helen";
function sayHi(){
let myName = "Mary";
console.log(myName); // Mary
function sayHi2(){
console.log(myName); // Mary
}
function sayHi3(){
let myname = "Lisa";
sayHi2();
}
sayHi3()
}
sayHi();
在 sayHi3()
裡執行 sayHi2()
時,因為 sayHi2()
並沒有定義 myName,所以會跑到 sayHi2()
被定義的地方(sayHi()裡)找,最終找到 myName 是 Mary
Call Stack工具
在執行 calling function 時會產生 stack
stack 是一種資料結構,可以想像成容器的概念,後面進去的要先出來
function fn1(){
console.log("This is fn1");
fn2();
function fn2(){
console.log("This is fn2")
fn3();
function fn3(){
console.log("This is fn3")
}
console.log("Running")
}
console.log("Done")
}
fn1();
// This is fn1
// This is fn2
// This is fn3
// Runnung
// Done